import { BaseEventType, LayerType, RepelType } from '../Config/BaseEnum';
import { _decorator, Component, instantiate, Node, Prefab, Sprite } from 'cc';
import { PanelBase } from '../UI/PanelBase';
import { LoadPanelBase } from '../UI/LoadPanelBase';
import { CallBackHandler } from '../Model/PoolObj/CallBackHandler';
import { LoadingManager } from './LoadingManager';
import { LogUtil } from '../../Util/LogUtil';
import { ButtonRedefine } from '../Component/ButtonRedefine';
import { EventManager } from './EventManager';
import { PoolFactory } from '../Component/PoolFactory';
import { Tool } from '../../Util/Tool';
const { ccclass } = _decorator;
/*eslint-disable*/
@ccclass('LayerManager')
export class LayerManager extends Component {
  static Singleton: LayerManager;
  blackMask: Node;
  layers: Map<number, Node> = new Map<number, Node>();
  factory: PoolFactory;

  /** 最大缓存面板数量 */
  private maxCachePanelNum: number = 5;
  /** 缓存界面 */
  private cachePanelList: PanelBase[] = [];
  /** 冒泡界面 */
  private bubblePanel: [string, Record<any, any>?][] = [];
  /** 正在显示的界面 */
  private showingPanels: Map<string, PanelBase> = new Map<string, PanelBase>();
  /** 当前加载界面 */
  private currentLoading: LoadPanelBase = null!;
  /** 所有Manager管理的界面 */
  private allControlPanels: Map<string, PanelBase> = new Map<string, PanelBase>();

  constructor() {
    super();
    LayerManager.Singleton = this;
  }

  init(): void {
    this.blackMask = this.node.getChildByName('blackMask');
    this.blackMask.removeFromParent();
    const layers = this.node.children;
    let index = 1;
    for (let i = 1; i < layers.length; i++) {
      const layer = layers[i];
      index = index << 1;
      this.layers.set(index, layer);
    }
    this.factory = this.node.getComponent(PoolFactory);

    EventManager.Ins().register(BaseEventType.GAME_SHOW_PANEL, this.openLoading, this);
    EventManager.Ins().register(BaseEventType.GAME_SHOW_PANEL_NOW, this.open, this);
    EventManager.Ins().register(BaseEventType.GAME_SHOW_PANEL_LATER, this.openPanelLater, this);
    EventManager.Ins().register(BaseEventType.GAME_SHOW_PANEL_NODE, this.showNode, this);
    EventManager.Ins().register(BaseEventType.GAME_CLOSE_PANEL, this.close, this);
  }

  initLoading(res: string) {
    if (this.currentLoading) {
      //如果要更换loading界面，应该只更改界面皮肤
      return;
    }
    const panel = instantiate(LoadingManager.Ins().getResource<Prefab>(res));
    this.currentLoading = panel.getComponent(LoadPanelBase);
    this.currentLoading.node.active = false;
    const layer = this.layers.get(LayerType.LoadingLayer);
    layer.addChild(panel);
  }

  /** 想放哪里就放哪里 */
  showNode(resPath: string, complete?: CallBackHandler) {
    const onComp = CallBackHandler.pop(this.loadPanelComplete, this, complete);
    LoadingManager.Ins().loadResource(resPath, onComp);
  }

  /* 直接显示节点 */
  private addNode<T extends PanelBase>(panel: T): void {
    const layer = this.layers.get(panel.panelLayer);
    if (panel.panelRepelType > RepelType.Self) {
      LogUtil.e('LayerManager=>openNode(): ' + panel.panelName + ' 直接显示界面一般是特殊功能界面，最好只设置成互斥或者不互斥');
    }
    const childrens = layer.children;
    let index = 0;
    for (let i = 0; i < childrens.length; i++) {
      const childSp: PanelBase = childrens[i].getComponent(PanelBase as any);
      if (childSp && childSp.panelIndex > panel.panelIndex) {
        index = i;
        break;
      } else {
        index++;
      }
    }
    if (panel.node.parent == layer) {
      if (panel.node.getSiblingIndex() != index) panel.node.setSiblingIndex(index);
    } else {
      layer.insertChild(panel.node, index);
    }
  }

  /** 不打开加载界面 */
  open(resPath: string, prop?: Record<any, any>) {
    const onComp = CallBackHandler.pop(this.loadPanelComplete, this, prop);
    LoadingManager.Ins().loadResource(resPath, onComp);
  }

  loadPanelComplete(res: Prefab[], prop?: Record<any, any>) {
    const panelSp: PanelBase = this.getPanelByPrefab(res[0]);
    if (!panelSp) return;
    if (prop) Tool.setNestedValue(panelSp, prop);
    this.allControlPanels.set(panelSp.panelName, panelSp);
    this.openPanel(panelSp);
  }

  getPanelByPrefab(prefab: Prefab): PanelBase {
    const panelSp: PanelBase = (prefab.data as Node).getComponent(PanelBase as any);
    if (!panelSp) throw new Error('界面没有绑定PanelBase');
    const panelName = panelSp.panelName;
    if (this.showingPanels.has(panelName)) {
      return null;
    }
    let result: PanelBase;
    const index = Tool.indexOfByProp(this.cachePanelList, 'panelName', panelName);
    if (index > -1) {
      result = this.cachePanelList.splice(index, 1)[0];
    }
    if (!result) {
      result = this.allControlPanels.get(panelName);
    }
    if (!result) {
      const panel = instantiate(prefab);
      result = panel.getComponent(PanelBase as any);
    }
    return result;
  }

  /** 打开加载界面 */
  openLoading(resPath: string, prop?: Record<any, any>) {
    if (!this.currentLoading) {
      this.open(resPath, prop);
      return;
    }
    this.currentLoading.loadPanel(resPath, prop);
  }

  /** 打开面板 只有会互斥的面板才有冒泡功能 */
  openPanelLater(paths: string, prop?: Record<any, any>) {
    if (this.showingPanels.size > 0) {
      this.bubblePanel.push([paths, prop]);
    } else {
      this.openLoading(paths, prop);
    }
  }

  /* 打开界面 */
  private openPanel<T extends PanelBase>(panel: T): void {
    if (panel.panelLayer == LayerType.LoadingLayer || panel.panelLayer == LayerType.GuideLayer || panel.panelLayer == LayerType.MenuLayer) {
      // LogUtil.e('LayerManager=>openPanel(): ' + panel.panelName + ' 加载加载层和引导层不应该受界面规则约束');
      this.addNode(panel);
      return;
    }
    this.resetPanelLayer(panel);
  }

  private resetPanelLayer(panel: PanelBase) {
    const panelName = panel.panelName;
    if (!this.showingPanels.has(panelName)) {
      const closePanle: PanelBase[] = [];
      if (panel.panelRepelType == RepelType.Self) {
        this.showingPanels.forEach((pp) => {
          if ((panel.panelLayer == pp.panelLayer && pp.panelRepelType != RepelType.None) || (panel.panelLayer & pp.panelRepelType) >= RepelType.Self) {
            closePanle.push(pp);
          }
        });
      } else if (panel.panelRepelType > RepelType.Self) {
        this.showingPanels.forEach((pp) => {
          if (pp.panelRepelType != RepelType.None && (pp.panelLayer & panel.panelRepelType) > RepelType.Self) {
            closePanle.push(pp);
          }
        });
      }
      closePanle.forEach((pp) => {
        pp.node.removeFromParent();
        const nn = pp.panelName;
        this.showingPanels.delete(nn);
        if (pp.autoRelease) {
          this.cachePanelList.push(pp);
        }
      });
      this.showingPanels.set(panelName, panel);
    }
    const layerNode = this.layers.get(panel.panelLayer);
    const childrens = layerNode.children;
    let index = 0;
    for (let i = 0; i < childrens.length; i++) {
      const childSp: PanelBase = childrens[i].getComponent(PanelBase as any);
      if (childSp && childSp.panelIndex > panel.panelIndex) {
        index = i;
        break;
      } else {
        index++;
      }
    }
    panel.node.removeFromParent();
    this.blackMask.removeFromParent();
    this.blackMask.layer = layerNode.layer;
    this.blackMask.getComponent(Sprite).color.set(0, 0, 0, panel.blackMask * 255);
    this.blackMask.getComponent(ButtonRedefine).isPierce = panel.isPierce;
    layerNode.insertChild(this.blackMask, index);
    layerNode.insertChild(panel.node, index + 1);
    this.releasePanel();
  }

  /**
   *
   * @param panelName 界面脚本类名
   * @param callback 关闭回调
   */
  close(panelName: string, callback?: CallBackHandler) {
    const panel: PanelBase = this.showingPanels.get(panelName);
    this.closePanel(panel, callback);
  }

  private closePanel(panel: PanelBase, callback?: CallBackHandler): void {
    const panelName = panel.panelName;
    this.showingPanels.delete(panelName);
    panel.onClose(callback);
    if (panel.autoRelease) {
      this.cachePanelList.push(panel);
      this.releasePanel();
    }
    let curPanel: PanelBase;
    let layer = LayerType.MIN;
    this.showingPanels.forEach((pp) => {
      if (pp.panelLayer > layer || (curPanel && pp.panelLayer == layer && pp.node.getSiblingIndex() > curPanel.node.getSiblingIndex())) {
        layer = pp.panelLayer;
        curPanel = pp;
      }
    });
    if (curPanel) {
      const layerNode = this.layers.get(curPanel.panelLayer);
      layerNode.addChild(this.blackMask);
      this.blackMask.getComponent(Sprite).color.set(0, 0, 0, curPanel.blackMask * 255);
      this.blackMask.getComponent(ButtonRedefine).isPierce = panel.isPierce;
      this.blackMask.setSiblingIndex(curPanel.node.getSiblingIndex());
    } else if (this.bubblePanel.length > 0) {
      this.blackMask.removeFromParent();
      const bubblePanel = this.bubblePanel.shift();
      this.openLoading(bubblePanel[0], bubblePanel[1]);
      return;
    } else {
      this.blackMask.removeFromParent();
    }
  }

  private releasePanel() {
    const count = this.cachePanelList.length - this.maxCachePanelNum;
    if (count <= 0) return;
    for (let i = 0; i < count; i++) {
      const panel = this.cachePanelList.shift();
      const panelName = panel.panelName;
      this.allControlPanels.delete(panelName);
      panel.destroy();
      panel.panelModel?.destroy();
    }
  }

  private onCloseTopPanel() {
    let temp = LayerType.MIN;
    this.showingPanels.forEach((pp) => {
      if (pp.panelLayer > temp) {
        temp = pp.panelLayer;
      }
    });
    if (temp == LayerType.MIN) return;
    const panelList = this.layers.get(temp).children;
    let panel: PanelBase;
    const len = panelList.length;
    for (let i = len - 1; i >= 0; i--) {
      const childSp: PanelBase = panelList[i].getComponent(PanelBase as any);
      if (childSp) {
        panel = childSp;
        break;
      }
    }
    if (panel && panel.clickBackClose) {
      this.closePanel(panel);
    }
  }
}
